package com.tools.web.work.excel.poi;

import com.tools.common.exception.IORuntimeException;
import com.tools.common.exception.InvalidOperationException;
import com.tools.common.io.IOKit;
import com.tools.common.object.*;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.*;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;

/**
 * POI Excel 相关操作的工具类
 * */
@Note("POI Excel 相关操作的工具类")
public final class ExcelKit {

    private ExcelKit() {}

    /* ************************************************************************************************************
     *
     *      读取已有的或生成新的空的 Workbook
     *
     * ************************************************************************************************************
     * */

    @Note("根据枚举值获取不同的 Excel 工作簿")
    public static Workbook newWorkbook(ExcelTypeEnum excelType) {
        return newWorkbook(excelType, SXSSFWorkbook.DEFAULT_WINDOW_SIZE, false);
    }

    @Note("根据枚举值获取不同的 Excel 工作簿。若指定生成 SXSSFWorkbook 则可设置 accessWindowSize 参数")
    public static Workbook newWorkbook(ExcelTypeEnum excelType, int accessWindowSize) {
        return newWorkbook(excelType, accessWindowSize, false);
    }

    @Note("根据枚举值获取不同的 Excel 工作簿。若指定生成 SXSSFWorkbook 则可设置 compressTmpFiles 参数")
    public static Workbook newWorkbook(ExcelTypeEnum excelType, boolean compressTmpFiles) {
        return newWorkbook(excelType, SXSSFWorkbook.DEFAULT_WINDOW_SIZE, compressTmpFiles);
    }


    @Note("根据枚举值获取不同的 Excel 工作簿。" +
            "accessWindowSize：允许 SXSSFWorkbook 保留在内存的条数，其余的写入磁盘。" +
            "compressTmpFiles：允许你控制是否压缩临时文件以减少磁盘空间的使用。")
    public static Workbook newWorkbook(ExcelTypeEnum excelType, int accessWindowSize, boolean compressTmpFiles) {
        switch (excelType) {
            case XSSF: return new XSSFWorkbook();
            case SXSSF: {
                XSSFWorkbook xssf = new XSSFWorkbook();
                return new SXSSFWorkbook(xssf, accessWindowSize, compressTmpFiles);
            }
            case HSSF: return new HSSFWorkbook();
            default: throw new IllegalArgumentException("不知道 " + excelType + " 的枚举类型的 Excel 格式");
        }
    }

    @Note("读取 Excel 文件，构建出 Workbook 对象")
    public static Workbook newWorkbook(ExcelTypeEnum excelType, InputStream excelFile) {
        return newWorkbook(excelType, excelFile, SXSSFWorkbook.DEFAULT_WINDOW_SIZE, false);
    }

    @Note("读取 Excel 文件，构建出 Workbook 对象。若最后指定生成 SXSSFWorkbook 则可指定 accessWindowSize 参数。")
    public static Workbook newWorkbook(ExcelTypeEnum excelType, InputStream excelFile, int accessWindowSize) {
        return newWorkbook(excelType, excelFile, accessWindowSize, false);
    }

    @Note("读取 Excel 文件，构建出 Workbook 对象。若指定生成 SXSSFWorkbook 则可设置 compressTmpFiles 参数")
    public static Workbook newWorkbook(ExcelTypeEnum excelType, InputStream excelFile, boolean compressTmpFiles) {
        return newWorkbook(excelType, excelFile, SXSSFWorkbook.DEFAULT_WINDOW_SIZE, compressTmpFiles);
    }

    @Note("读取 Excel 文件，构建出 Workbook 对象。" +
            "accessWindowSize 是 SXSSFWorkbook 保留在内存的条数，其余的写入磁盘。" +
            "compressTempFiles 参数允许你控制是否压缩临时文件以减少磁盘空间的使用")
    public static Workbook newWorkbook(ExcelTypeEnum excelType, InputStream excelFile, int accessWindowSize, boolean compressTmpFiles) {
        try {
            switch (excelType) {
                case XSSF: return new XSSFWorkbook(excelFile);
                case HSSF: return new HSSFWorkbook(excelFile);
                case SXSSF: {
                    XSSFWorkbook xssf = new XSSFWorkbook(excelFile);
                    return new SXSSFWorkbook(xssf, accessWindowSize, compressTmpFiles);
                }
                default: throw new IllegalArgumentException("不知道 " + excelType + " 的枚举类型的 Excel 格式");
            }
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }

    /* ************************************************************************************************************
     *
     *      公共逻辑
     *
     * ************************************************************************************************************
     * */

    @Note("根据名称创建 Sheet 页，如果名称为空就用默认的")
    public static Sheet newSheet(Workbook workbook, String sheetName) {
        return (sheetName == null || sheetName.isEmpty())
                ? workbook.createSheet()
                : workbook.createSheet(sheetName);
    }

    @Note("给 Excel 单元格的设置一些默认样式，返回样式对象")
    public static CellStyle defaultCellStyle(Workbook workbook) {
        if(workbook == null) throw new NullPointerException("Excel 工作簿对象为 null");
        CellStyle cellStyle = workbook.createCellStyle();
        //单元格内容自动换行
        cellStyle.setWrapText(true);
        //单元格内容[垂直]方向的对齐样式为[居中]
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        //单元格内容[水平]方向的对齐样式为[居中]
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        return cellStyle;
    }

    @Note("返回一个永远不可能为 null 的单元格样式对象")
    public static CellStyle getOrCellStyle(CellStyle cellStyle, Workbook workbook) {
        if(cellStyle != null) return cellStyle;
        return defaultCellStyle(workbook);
    }

    @Note("返回一个永远不可能为 null 的表头样式对象")
    public static CellStyle getOrHeaderStyle(CellStyle headStyle, Workbook workbook) {
        if(headStyle != null) return headStyle;
        return defaultHeaderStyle(workbook);
    }

    @Note("专门为表头单元格设置的默认样式")
    public static CellStyle defaultHeaderStyle(Workbook workbook) {
        CellStyle cellStyle = defaultCellStyle(workbook);
        //设置表头字体加粗、字体名称、字高、字体的颜色
        Font font = workbook.createFont();
        font.setBold(true);
        font.setFontName("黑体");
        font.setFontHeightInPoints((short) 14);
        font.setColor((short) 0);
        cellStyle.setFont(font);
        cellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        // 设置单元格的边框样式
        cellStyle.setBorderTop(BorderStyle.THIN);
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);

        // 设置单元格的边框颜色
        cellStyle.setTopBorderColor(IndexedColors.BLACK.getIndex());
        cellStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex());
        cellStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex());
        cellStyle.setRightBorderColor(IndexedColors.BLACK.getIndex());
        return cellStyle;
    }


    @Note("将注解标准的表头配置转为 ExcelField 列表")
    public static List<ExcelField> buildExcelFieldListByAnnotation(Class<?> entityClass) {
        if(entityClass == null) return new ArrayList<>();
        ExcelEntity a = entityClass.getAnnotation(ExcelEntity.class);
        if(a == null) return new ArrayList<>();
        Map<String, Field> fieldMap = ObjKit.allFields(entityClass);
        List<ExcelField> result = new ArrayList<>(fieldMap.size());
        TreeMap<Integer, Object[]> map = new TreeMap<>();
        for (Map.Entry<String, Field> e : fieldMap.entrySet()) {
            Field field = e.getValue();
            ExcelColumn ca = field.getAnnotation(ExcelColumn.class);
            if(ca == null) continue;
            int sort = ca.sort();
            map.put(sort, new Object[] {ca, e.getKey()});
        }
        for (Map.Entry<Integer, Object[]> e : map.entrySet()) {
            Object[] o = e.getValue();
            ExcelColumn ca = (ExcelColumn) o[0];
            result.add(new ExcelField(ca.width(), ca.sameValueMerge(), (String) o[1], ca.names()));
        }
        return result;
    }

    /* ************************************************************************************************************
     *
     *      导出的各个步骤的工具方法
     *
     * ************************************************************************************************************
     * */


    @Note("生成表头，若又合并则合并")
    public static void buildSheetTableHead(Sheet sheet, Workbook workbook, SheetExporter sheetExporter) {
        CellStyle headStyle = getOrHeaderStyle(sheetExporter.getHeadStyle(), workbook);
        ExcelHead excelHead = sheetExporter.getExcelHead();
        if(excelHead == null) {
            throw new NullPointerException("ExcelHead 表头配置为 null");
        }
        List<ExcelField> headRowList = excelHead.getHeadRows();
        int maxHeadRowNum = excelHead.getMaxHeadRowNum();
        if(headRowList == null || headRowList.isEmpty() || maxHeadRowNum == 0) {
            throw new InvalidOperationException("ExcelHead 属性配置的表头数据项为空，无法设置表头");
        }
        //生成表头以及合并该有的单元格
        int headHeight = sheetExporter.getHeadHeight();
        Map<String, MergeCell> headMergeMap = buildExcelHeadAndGetMergeMap(excelHead, sheet,
                headStyle, headHeight, sheetExporter.getIgnoreFieldSet());
        defaultMergeHeadHandle(headMergeMap, sheet);
    }

    @Note("创建基础的表头行列，同时返回需要合并的单元格行列索引信息 Map")
    public static Map<String, MergeCell> buildExcelHeadAndGetMergeMap(ExcelHead excelHead, Sheet sheet,
                                                                      CellStyle headStyle, int headHeight,
                                                                      Set<String> ignoreFieldSet) {
        int maxHeadRowNum = excelHead.getMaxHeadRowNum();
        Row[] headRowArray = new Row[maxHeadRowNum];
        for (int i = 0; i < maxHeadRowNum; i++) {
            Row row = sheet.createRow(i);
            row.setHeight((short) (headHeight * 20));
            headRowArray[i] = row;
        }
        List<ExcelField> headRowList = excelHead.getHeadRows();
        Map<String, MergeCell> map = new HashMap<>();
        for (int i = 0; i < headRowList.size(); i++) {
            ExcelField field = headRowList.get(i);
            String property = field.getField();
            if(ignoreFieldSet != null && ignoreFieldSet.contains(property)) {
                continue;
            }
            String[] names = field.getNames();
            int length = names.length;
            for (int j = 0; j < maxHeadRowNum; j++) {
                if(j < length) {
                    String name = names[j];
                    String keyPrefix = field.getField() + '-' + name;
                    MergeCell mergeCell = map.get(keyPrefix);
                    if(mergeCell == null) {
                        mergeCell = new MergeCell();
                        map.put(keyPrefix, mergeCell);
                        mergeCell.setStartCol(i);
                        mergeCell.setEndCol(i);
                        mergeCell.setStartRow(j);
                        mergeCell.setEndRow(j);
                        Cell cell = headRowArray[j].createCell(i);
                        cell.setCellStyle(headStyle);
                        cell.setCellValue(name);
                        sheet.setColumnWidth(i, field.getHeadWidth() * 256);
                        continue;
                    }
                    mergeCell.setEndCol(i);
                    mergeCell.setEndRow(j);
                    continue;
                }
                Cell cell = headRowArray[j].createCell(i);
                cell.setCellStyle(headStyle);
                cell.setCellValue("");
                sheet.setColumnWidth(i, field.getHeadWidth() * 256);
                String name = names[length - 1];
                String keyPrefix = field.getField() + '-' + name;
                MergeCell mergeCell = map.get(keyPrefix);
                if(mergeCell == null) {
                    mergeCell = new MergeCell();
                    map.put(keyPrefix, mergeCell);
                    mergeCell.setStartCol(i);
                    mergeCell.setEndCol(i);
                    mergeCell.setEndRow(j);
                    continue;
                }
                mergeCell.setEndCol(i);
                mergeCell.setEndRow(j);
            }
        }
        return map;
    }


    @Note("默认进行表头合并的操作")
    public static void defaultMergeHeadHandle(Map<String, MergeCell> map, Sheet sheet) {
        if(map == null || map.isEmpty()) return;
        for (Map.Entry<String, MergeCell> e : map.entrySet()) {
            MergeCell mc = e.getValue();
            if(mc.isSingleCell()) {
                continue;
            }
            sheet.addMergedRegion(new CellRangeAddress(
                    mc.getStartRow(), mc.getEndRow(),
                    mc.getStartCol(), mc.getEndCol()
            ));
        }
    }


    @Note("计算 Excel 单元格所需的真正行高值")
    public static short getRowHeight(int rowHeight) {
        return (short) (rowHeight * 20);
    }

    @Note("计算 Excel 单元格所需的真正列宽值")
    public static int getCellWidth(int cellWidth) {
        return cellWidth * 256;
    }


    @Note("写入数据到指定的 Sheet 中")
    public static void buildData(List<?> data, Sheet sheet, SheetExporter sheetExporter,
                                 Workbook workbook, ExcelExporter excelExporter) {
        if(data == null || data.isEmpty()) {
            return;
        }
        int maxHeadRowNum = sheetExporter.getMaxHeadRowNum();
        RowWriteHandler rowWriteHandler = sheetExporter.getRowWriteHandler();
        CellStyle cellStyle = getOrCellStyle(sheetExporter.getCellStyle(), workbook);
        if(rowWriteHandler != null) {
            for (int i = 0; i < data.size(); i++) {
                Object o = data.get(i);
                Row row = sheet.createRow(maxHeadRowNum + i);
                row.setHeight(sheetExporter.calcRowHeight());
                rowWriteHandler.handle(o, row, cellStyle, sheetExporter, excelExporter);
            }
            return;
        }
        ExcelHead excelHead = sheetExporter.getExcelHead();
        List<ExcelField> headRowList = excelHead.getHeadRows();
        Map<Object, List<MergeCell>> map = writeData(sheet, maxHeadRowNum, cellStyle, workbook,
                sheetExporter.getMergeCellIndexMap(), sheetExporter.isSameMergeOfAllField(),
                data, headRowList, sheetExporter.getRowHeight(), sheetExporter.getIgnoreFieldSet());
        sheetExporter.setMergeCellIndexMap(map);
    }



    @Note("默认进行数据表的行（相邻且值相同）合并的操作")
    public static void defaultMergeTableHandle(Map<Object, List<MergeCell>> map, Sheet sheet) {
        if(map == null || map.isEmpty()) return;
        for (Map.Entry<Object, List<MergeCell>> e : map.entrySet()) {
            List<MergeCell> mergeCellList = e.getValue();
            for (MergeCell m : mergeCellList) {
                if(m.isSingleCell()) {
                    continue;
                }
                sheet.addMergedRegion(new CellRangeAddress(m.getStartRow(),
                        m.getEndRow(), m.getStartCol(), m.getEndCol()));
            }
        }
    }


    @Note("以普通的方式填充数据进 Excel，并且合并单元格")
    public static Map<Object, List<MergeCell>> writeData(Sheet sheet, int startRowNum, CellStyle cellStyle, Workbook workbook,
                                                         Map<Object, List<MergeCell>> map, boolean sameMergeOfAllField,
                                                         List<?> data, List<ExcelField> headRowList, int rowHeight,
                                                         Set<String> ignoreFieldSet) {
        if(map == null) {
            map = new HashMap<>();
        }
        if(data.isEmpty()) {
            return map;
        }
        Map<String, Field> fieldMap = ObjKit.allFields(data.get(0).getClass());
        for (int i = 0; i < data.size(); i++) {
            Object o = data.get(i);
            int rowIndex = i + startRowNum;
            Row row = sheet.createRow(rowIndex);
            row.setHeight((short) (rowHeight * 20));
            for (int j = 0; j < headRowList.size(); j++) {
                ExcelField ef = headRowList.get(j);
                String field = ef.getField();
                if(ignoreFieldSet != null && ignoreFieldSet.contains(field)) {
                    continue;
                }
                Cell cell = row.createCell(j);
                cell.setCellStyle(cellStyle);
                sheet.setColumnWidth(j, ef.getHeadWidth() * 256);
                private_putMergeIndexAndInputValue(cell, fieldMap.get(field), o,
                        workbook, sheet, sameMergeOfAllField, ef.isSameValueMerge(), rowIndex, j,  map);
            }
        }
        return map;
    }


    @Note("记录合并行单元格的索引数据")
    public static void recordMergeRowIndex(Map<Object, List<MergeCell>> map, int rowIndex,
                                           int columnIndex, String prefix, Object value) {
        if(value == null) {
            return;
        }
        if(value instanceof String) {
            String v = (String) value;
            if(v.isEmpty()) return;
        }
        String key = prefix + value;
        List<MergeCell> mergeCellList = map.computeIfAbsent(key, k -> new ArrayList<>());;
        if(mergeCellList.isEmpty()) {
            MergeCell mc1 = new MergeCell();
            mc1.setStartRow(rowIndex);
            mc1.setEndRow(rowIndex);
            mc1.setStartCol(columnIndex);
            mc1.setEndCol(columnIndex);
            mergeCellList.add(mc1);
            return;
        }
        MergeCell mc2 = mergeCellList.get(mergeCellList.size() - 1);
        int endRow = mc2.getEndRow();
        if(endRow == (rowIndex - 1)) {
            mc2.setEndRow(rowIndex);
            return;
        }
        MergeCell mc3 = new MergeCell();
        mc3.setStartRow(rowIndex);
        mc3.setEndRow(rowIndex);
        mc3.setStartCol(columnIndex);
        mc3.setEndCol(columnIndex);
        mergeCellList.add(mc3);
    }

    /* ************************************************************************************************************
     *
     *      导入的各个步骤的工具方法
     *
     * ************************************************************************************************************
     * */

    @Note("判断要 set 的字段的类型是否是可转为图片类型")
    public static boolean isImageType(Class<?> fieldType) {
        return InputStream.class.isAssignableFrom(fieldType) ||
                BufferedImage.class.isAssignableFrom(fieldType) ||
                File.class.isAssignableFrom(fieldType) ||
                URL.class.isAssignableFrom(fieldType);
    }

    @Note("读取 Sheet 内的所有图片")
    public static Map<Integer, ExcelImg> readImgAsColumnIndexMap(Sheet sheet) {
        if(sheet instanceof XSSFSheet) {
            return private_readXSSFImage((XSSFSheet) sheet);
        }
        if(sheet instanceof HSSFSheet) {
            return private_readHSSFImage((HSSFSheet) sheet);
        }
        throw new IllegalArgumentException("无法从 " + sheet.getClass().getName() + " 类型的工作簿中读取图片数据");
    }

    @Note("将 Excel 的第 startRow 行到第 endRow 行的 Row 对象转为 JavaBean 对象，然后放到 List 集合里返回")
    public static <T> List<T> readToList(ExcelImporter excelImporter, SheetImporter sheetImporter,
                                         Sheet sheet, int startRow, int endRow, C<T> dataType) {
        RowReadHandler<T> rowReadHandler = sheetImporter.getRowReadHandler(dataType.getMainType());
        List<T> data = new ArrayList<>(excelImporter.readCount());
        if(rowReadHandler != null) {
            for (int i = startRow; i <= endRow; i++) {
                try {
                    Row row = sheet.getRow(i);
                    T obj = rowReadHandler.handle(sheetImporter, row, excelImporter);
                    data.add(obj);
                } catch (Exception e) {
                    int sheetIndex = excelImporter.getSheetIndex();
                    excelImporter.addError("第 " + sheetIndex + " 页第 " + i + " 行",
                            "整行内部发生错误, 错误原因: " + e.getMessage());
                }
            }
            return data;
        }
        for (int i = startRow; i <= endRow; i++) {
            try {
                Row row = sheet.getRow(i);
                T obj = ObjKit.buildObject(row, sheetImporter, excelImporter, dataType);
                data.add(obj);
            } catch (Exception e) {
                int sheetIndex = excelImporter.getSheetIndex();
                excelImporter.addError("第 " + sheetIndex + " 页第 " + i + " 行",
                        "整行数据反射生成对象并填充字段的过程出错, 错误原因: " + e.getMessage());
            }
        }
        return data;
    }


    /* ************************************************************************************************************
     *
     *      私有逻辑
     *
     * ************************************************************************************************************
     * */

    @Note("若类型是图片，则在此处导出图片至 Excel")
    private static void private_writeImageIn(Object imageObj, Workbook workbook, Sheet sheet,
                                             int startRow, int endRow, int startCol, int endCol) {
        try {
            int pictureIdx;
            if(imageObj instanceof InputStream) {
                InputStream imageInput = (InputStream) imageObj;
                byte[] bytes = IOUtils.toByteArray(imageInput);
                pictureIdx = workbook.addPicture(bytes, Workbook.PICTURE_TYPE_JPEG);
                imageInput.close();
            } else {
                BufferedImage image = (BufferedImage) imageObj;
                byte[] bytes = IOKit.imageToByteArray(image, "png");
                pictureIdx = workbook.addPicture(bytes, Workbook.PICTURE_TYPE_JPEG);
            }
            // 创建绘图对象
            Drawing<?> drawing = sheet.createDrawingPatriarch();
            // 创建锚点
            ClientAnchor anchor;
            if(sheet instanceof XSSFSheet) {
                anchor = new XSSFClientAnchor();
            } else {
                anchor = new HSSFClientAnchor();
            }
            anchor.setCol1(startCol); // 起始列
            anchor.setRow1(startRow); // 起始行
            anchor.setCol2(endCol); // 终止列
            anchor.setRow2(endRow); // 终止行
            // 插入图片
            Picture picture = drawing.createPicture(anchor, pictureIdx);
            //设置图片的大小，使用宽高缩放因子决定图片的缩放比例
            picture.resize(1.0, 1.0);
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }

    @Note("读取 XSSFWorkbook 的 Sheet 内部的所有图片")
    private static Map<Integer, ExcelImg> private_readXSSFImage(XSSFSheet sheet) {
        XSSFDrawing drawing = sheet.createDrawingPatriarch();
        List<XSSFShape> shapes = drawing.getShapes();
        int size = shapes.size();
        Map<Integer, ExcelImg> indexAndImgMap = new HashMap<>((int) ((size / 0.75f) + 1));
        for (XSSFShape shape : shapes) {
            XSSFPicture picture = (XSSFPicture) shape;
            XSSFClientAnchor anchor = (XSSFClientAnchor) picture.getAnchor();
            XSSFPictureData pictureData = picture.getPictureData();
            int startCol = anchor.getCol1();
            ExcelImg excelImg = new ExcelImg(pictureData, anchor.getRow1(), anchor.getRow2());
            indexAndImgMap.put(startCol, excelImg);
            for (int j = startCol + 1; j < anchor.getCol2(); j++) {
                indexAndImgMap.put(j, excelImg);
            }
        }
        return indexAndImgMap;
    }


    @Note("读取 HSSFWorkbook 的 Sheet 内部的所有图片")
    private static Map<Integer, ExcelImg> private_readHSSFImage(HSSFSheet sheet) {
        HSSFPatriarch patriarch = sheet.getDrawingPatriarch();
        List<HSSFShape> pictures = patriarch.getChildren();
        int size = pictures.size();
        Map<Integer, ExcelImg> indexAndImgMap = new HashMap<>((int) ((size / 0.75f) + 1));
        for (HSSFShape shape : pictures) {
            if(shape instanceof HSSFPicture) {
                HSSFPicture picture = (HSSFPicture) shape;
                HSSFPictureData pictureData = picture.getPictureData();
                HSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor();
                int col1 = anchor.getCol1();
                int row1 = anchor.getRow1();
                int col2 = anchor.getCol2();
                int row2 = anchor.getRow2();
                ExcelImg excelImg = new ExcelImg(pictureData, row1, row2);
                indexAndImgMap.put(col1, excelImg);
                for (int j = col1 + 1; j < col2; j++) {
                    indexAndImgMap.put(j, excelImg);
                }
            }
        }
        return indexAndImgMap;
    }


    @Note("记录合并行单元格的索引数据，然后填充单元格的数据值")
    private static void private_putMergeIndexAndInputValue(Cell cell, Field field, Object obj,
                                                           Workbook workbook, Sheet sheet,
                                                           boolean sameMergeOfAllField, boolean sameMerge,
                                                           int rowIndex, int columnIndex, Map<Object, List<MergeCell>> map) {
        if(field == null) return;
        Object value = ObjKit.getFieldValue(obj, field);
        if(sameMerge || sameMergeOfAllField) {
            recordMergeRowIndex(map, rowIndex, columnIndex, field.getName() + '-', value);
        }
        Class<?> type = field.getType();
        private_inputValue(cell, type, value, workbook, sheet, rowIndex, columnIndex);
    }



    @Note("根据类型的不同，将属性值转为不同的 Excel 单元格值的类型放入单元格 cell 中")
    private static void private_inputValue(Cell cell, Class<?> type, Object value, Workbook workbook,
                                           Sheet sheet, int rowIndex, int columnIndex) {
        if(value == null) {
            cell.setBlank();
            return;
        }
        if(CharSequence.class.isAssignableFrom(type) || Character.class.equals(type) || char.class.equals(type)) {
            cell.setCellValue(ObjKit.toStringEmptyString(value));
            return;
        }
        if(Boolean.class.equals(type) || boolean.class.equals(type)) {
            cell.setCellValue(BasicEnum.booleanValue(value));
            return;
        }
        if(BasicEnum.isBasicOrWrapper(type)) {
            cell.setCellValue(((Number) value).doubleValue());
            return;
        }
        if(Date.class.isAssignableFrom(type)) {
            cell.setCellValue(DateKit.format((Date) value));
            return;
        }
        if(LocalDateTime.class.isAssignableFrom(type)) {
            cell.setCellValue(DateKit.format((LocalDateTime) value));
            return;
        }
        if(LocalDate.class.isAssignableFrom(type)) {
            cell.setCellValue(DateKit.format((LocalDate) value));
            return;
        }
        if(LocalTime.class.isAssignableFrom(type)) {
            cell.setCellValue(DateKit.format((LocalTime) value));
            return;
        }
        if(InputStream.class.isAssignableFrom(type)) {
            private_writeImageIn(value, workbook, sheet, rowIndex, rowIndex, columnIndex, columnIndex);
            return;
        }
        if(BufferedImage.class.isAssignableFrom(type)) {
            private_writeImageIn(value, workbook, sheet, rowIndex, rowIndex, columnIndex, columnIndex);
        }
        if(File.class.isAssignableFrom(type)) {
            FileInputStream fileInput = IOKit.fileInputStream((File) value);
            private_writeImageIn(fileInput, workbook, sheet, rowIndex, rowIndex, columnIndex, columnIndex);
            return;
        }
        if(URL.class.isAssignableFrom(type)) {
            InputStream urlInput = IOKit.urlInputStream((URL) value);
            private_writeImageIn(urlInput, workbook, sheet, rowIndex, rowIndex, columnIndex, columnIndex);
            return;
        }
        cell.setCellValue(ObjKit.toStringEmptyString(value));
    }
}
